using Unity.Mathematics;

namespace Geometry
{

   public class QuadtreeTilingScheme
    {
        Rectangle _rectangle;
        int _rootTilesX;
        int _rootTilesY;

        /**
         * @brief Constructs a new instance.
         *
         * @param rectangle The overall rectangle that is tiled, expressed in
         * projected coordinates.
         * @param rootTilesX The number of tiles at the root of the quadtree in the X
         * direction.
         * @param rootTilesY The nubmer of tiles at the root of the quadtree in the Y
         * direction.
         */
        public  QuadtreeTilingScheme(
            Rectangle rectangle,
            int rootTilesX,
            int rootTilesY)
        {
            _rectangle = rectangle;
            _rootTilesX = rootTilesX;
            _rootTilesY = rootTilesY;
        }

        /**
         * @brief Return the overall rectangle that is tiled.
         *
         * The rectangle is expressed in projected coordinates.
         */
      public  Rectangle getRectangle()
        {
            return this._rectangle;
        }

        /**
         * @brief Returns the number of root tiles, in x-direction.
         */
      public  int getRootTilesX() { return this._rootTilesX; }

        /**
         * @brief Returns the number of root tiles, in y-direction.
         */
     public   int getRootTilesY() { return this._rootTilesY; }

        /**
         * @brief Returns the number of tiles, in x-direction, at the given level.
         */
        public int getNumberOfXTilesAtLevel(int level)
        {
            return this._rootTilesX << level;
        }

        /**
         * @brief Returns the number of tiles, in y-direction, at the given level.
         */
        public int getNumberOfYTilesAtLevel(int level)
        {
            return this._rootTilesY << level;
        }

        /**
         * @brief Computes the {@link QuadtreeTileID} for a given
         * position and level.
         *
         * If the given position is within the {@link getRectangle} of this tiling
         * scheme, then this will compute the quadtree tile ID for the tile that
         * contains the given position at the given level. Otherwise, `nullopt`
         * is returned.
         *
         * @param position The 2D position
         * @param level The level
         * @return The tile ID, or `nullopt`.
         */
        public QuadtreeTileID? positionToTile(double2 position, int level)
        {
            if (!this.getRectangle().contains(position))
            {
                // outside the bounds of the tiling scheme
                return null;
            }

            int xTiles = this.getNumberOfXTilesAtLevel(level);
            int yTiles = this.getNumberOfYTilesAtLevel(level);

            double overallWidth = this.getRectangle().computeWidth();
            double xTileWidth = overallWidth / xTiles;
            double overallHeight = this.getRectangle().computeHeight();
            double yTileHeight = overallHeight / yTiles;

            double distanceFromWest = position.x - this.getRectangle().minimumX;
            double distanceFromSouth = position.y - this.getRectangle().minimumY;

            int xTileCoordinate = (int)(distanceFromWest / xTileWidth);
            if (xTileCoordinate >= xTiles)
            {
                xTileCoordinate = xTiles - 1;
            }
            int yTileCoordinate = (int)(distanceFromSouth / yTileHeight);
            if (yTileCoordinate >= yTiles)
            {
                yTileCoordinate = yTiles - 1;
            }

            return new QuadtreeTileID(
                level,
                xTileCoordinate,
                yTileCoordinate);
        }

        /**
         * @brief Returns the {@link Rectangle} that is covered by the
         * specified tile.
         *
         * The rectangle that is covered by the tile that is identified with
         * the given {@link QuadtreeTileID} will be computed,
         * based on the {@link getRectangle} of this tiling scheme.
         *
         * @param tileID The tile ID
         * @return The rectangle
         */
        public Rectangle tileToRectangle(QuadtreeTileID tileID)
        {
            int xTiles = this.getNumberOfXTilesAtLevel(tileID.level);
            int yTiles = this.getNumberOfYTilesAtLevel(tileID.level);

            double xTileWidth = this._rectangle.computeWidth() / xTiles;
            double left = this._rectangle.minimumX + tileID.x * xTileWidth;
            double right = this._rectangle.minimumX + (tileID.x + 1) * xTileWidth;

            double yTileHeight = this._rectangle.computeHeight() / yTiles;
            double bottom = this._rectangle.minimumY + tileID.y * yTileHeight;
            double top = this._rectangle.minimumY + (tileID.y + 1) * yTileHeight;

            return new Rectangle(left, bottom, right, top);
        }

    }
}